public boolean add(E e) { return offer(e); } // 调用的是offer public boolean offer(E e) { if (e == null) throw new NullPointerException(); // 几乎所有的集合类都有这个,顾名思义,主要是记录修改次数,实际上是为了防止你在遍历的时候更改了数据,造成不一致,会抛出ConcurrentModificationException 异常,注意,这与并发没有多大联系 modCount++; int i = size; // 检查是否需要扩容,queue就是真实数据 if (i >= queue.length) grow(i + 1); size = i + 1; if (i == 0) queue[0] = e; else // 使用经典的 siftUp 上移最后添加的元素, 保证我们的堆还是有序的 siftUp(i, e); return true; }
删除元素
1 2 3 4 5 6 7 8 9 10 11 12 13
// 同样使用 siftDown,首先将最后一个元素移到堆顶,再调整堆即可 public E poll() { if (size == 0) return null; int s = --size; modCount++; E result = (E) queue[0]; E x = (E) queue[s]; queue[s] = null; if (s != 0) siftDown(0, x); return result; }
// 因为使用的是最堆的数据结构,所以只能遍历,效率较低 private int indexOf(Object o) { if (o != null) { for (int i = 0; i < size; i++) if (o.equals(queue[i])) return i; } return -1; }
关于siftUp
1 2 3 4 5 6 7
// 如果没有外排序,则使用内排序 private void siftUp(int k, E x) { if (comparator != null) siftUpUsingComparator(k, x); else siftUpComparable(k, x); }
简单看一个函数,复习一下堆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
private void siftUpComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>) x; while (k > 0) { // 一如既往使用位运算提高效率 int parent = (k - 1) >>> 1; Object e = queue[parent]; // 如果父元素小的话,说明最小堆已经都建好了, // 否则交换继续调整 if (key.compareTo((E) e) >= 0) break; queue[k] = e; k = parent; } queue[k] = key; }
你可能遇到的坑
如果你中途改变了数据,有关排序的字段,最小堆是不会自动重新构建,那么,这个优先级队列就会失效!!!!!!举个例子,你把一堆学生按身高放入优先级队列,突然,有几个学生的身高改变了,这个最小堆是不会重构的,这时候得不到你想要的值,那有没有重构的函数,抱歉,没有!heapify 是私有的函数,但是你可以通过 new PriorityQueue(queue),重新构建一个